package com.personal.dataanalyse.reportmanage.builder.impl;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.personal.core.bean.TwoTuple;
import com.personal.core.data.DataColumn;
import com.personal.core.data.DataRow;
import com.personal.core.data.DataTable;
import com.personal.core.utils.CoreUtil;
import com.personal.core.utils.StringUtil;
import com.personal.dataanalyse.consts.FieldConsts;
import com.personal.dataanalyse.enums.FieldDataTypeEnum;
import com.personal.dataanalyse.enums.ModelFieldTypeEnum;
import com.personal.dataanalyse.enums.TotalTypeEnum;
import com.personal.dataanalyse.reportmanage.base.DataAnalyseModel;
import com.personal.dataanalyse.reportmanage.builder.ModelFiller;
import com.personal.dataanalyse.reportmanage.entity.ConditionInfo;
import com.personal.dataanalyse.reportmanage.entity.DataColumnReportEx;
import com.personal.dataanalyse.reportmanage.entity.DataRowReportEx;


/**
 * 默认的模型填充者
 * @author cuibo
 *
 */
public class DefaultModelFiller implements ModelFiller<DataAnalyseModel>
{
    
    /** 报表结构 */
    private DataTable structTable;
    
    /** 所有指标 */
    private DataTable table;
    
    /** 分析模型 */
    private DataAnalyseModel model;
    
    public DefaultModelFiller()
    {
        super();
    }

    /**
     * 填充
     * @return
     */
    @Override
    @SuppressWarnings("unchecked")
    public void fill()
    {
        if (structTable == null || table == null || table.getRows().isEmpty())
        {
            return;
        }
        // 是否定义纵向维度
        boolean hasyGroup = structTable.getRows() != null && !structTable.getRows().isEmpty();
        // 每个Row中都存在一个缓存Map，存储必要信息：每行每列中，每个细指标的合计。每行每列中，匹配的个数
        Map<String, Object> cacheMap = null;
        if (hasyGroup)
        {
            // 给每行添加一个缓存Map
            for (DataRow srow : structTable.getRows())
            {
                cacheMap = new HashMap<String, Object>();
                srow.getItemMap().put(FieldConsts.STR_CACHE, cacheMap);
            }
            // 用于求占比的行
            Map<DataColumn, Set<DataRow>> zbCacheMap = new HashMap<DataColumn, Set<DataRow>>();
            for (DataColumn dataColumn : structTable.getColumns())
            {
                zbCacheMap.put(dataColumn, new HashSet<DataRow>());
            }
            
            // 填充第一次循环就可以计算出的数据
            fillFirstCanCalData(zbCacheMap);
            // 对于先汇总后计算的数据和求占比的数据和求平均值的数据需要再次处理
            for (DataRow srow : structTable.getRows())
            {
                // 再次处理
                cacheMap = (Map<String, Object>)srow.getItemMap().get(FieldConsts.STR_CACHE);
                TotalTypeEnum.reHandleTotalType(srow, cacheMap, structTable.getColumns());
            }
            // 缓存求占比的合计，每列的占比合计
            Map<String, Object> zbHjMap = new HashMap<String, Object>();
            TotalTypeEnum.calZbHjMap(structTable.getColumns(), zbCacheMap, zbHjMap);
            
            // 求出占比
            for (DataRow srow : structTable.getRows())
            {
                // 再次处理
                TotalTypeEnum.calProportion(srow, structTable.getColumns(), zbHjMap);
            }
            // 处理计算列
            handleCalColumn();
        } else
        {
            int rowIndex = -1;
            for (DataRow row : table.getRows())
            {
                Map<String, Object> map = row.getItemMap();
                if (map == null || map.isEmpty())
                {
                    continue;
                }
                rowIndex ++;
                fillNoyRow(map, rowIndex);
            }
        }
    }

    @SuppressWarnings("unchecked")
    private void fillFirstCanCalData(Map<DataColumn, Set<DataRow>> zbCacheMap)
    {
        // 遍历报表结构行
        DataRowReportEx rowReportEx = null;
        // 缓存Map
        Map<String, Object> cacheMap = null;
        // 跳跃数
        int skipCount = 0;
        // yNoRepeat的符合行数
        int yNoRepeatCount = -1;
        
        for (DataRow row : table.getRows())
        {
            // 缓存条件的结果
            Map<ConditionInfo, Boolean> conditionResultMap = new HashMap<ConditionInfo, Boolean>();
            yNoRepeatCount = -1;
            skipCount = 0;
            for (DataRow srow : structTable.getRows())
            {
                if (yNoRepeatCount > -1)
                {
                    yNoRepeatCount --;
                }
                // 符合数到0之后可以直接跳出循环
                if (yNoRepeatCount == 0)
                {
                    break;
                }
                
                // 大于1的原因是因为判断跳跃之前已经循环了一次
                if (skipCount > 1)
                {
                    skipCount --;
                    continue;
                }
                if (!(srow instanceof DataRowReportEx))
                {
                    continue;
                }
                cacheMap = (Map<String, Object>) srow.getItemMap().get(FieldConsts.STR_CACHE);
                rowReportEx = (DataRowReportEx) srow;
                // 行不匹配直接返回
                TwoTuple<Boolean, Integer> two = checkMatchCondition(row.getItemMap(), rowReportEx.getConditions(), conditionResultMap);
                if (!two.getA())
                {
                    skipCount = two.getB();
                    continue;
                }
                // 设置匹配标记为true
                rowReportEx.setMatchData(true);
                fillHasyRow(rowReportEx, row, cacheMap, zbCacheMap);
                
                // 如果是在Y方向不重复维度，则可以直接下一行数据
                if (model.isyNoRepeat() && yNoRepeatCount == -1)
                {
                    yNoRepeatCount = two.getB();
                }
            }
        }
    }

    /**
     * 计算计算列
     */
    private void handleCalColumn()
    {
        for (DataRow srow : structTable.getRows())
        {
            for (DataColumn dataColumn : structTable.getColumns())
            {
                if (!(dataColumn instanceof DataColumnReportEx))
                {
                    continue;
                }
                DataColumnReportEx columnEx = (DataColumnReportEx) dataColumn;
                if (ModelFieldTypeEnum.计算列.toString().equals(columnEx.getFieldType()))
                {
                    // 计算列是依据其计算的列来判断其是否匹配上
                    srow.setValue(dataColumn, CoreUtil.calculateExByAviWithAbs(columnEx.getFieldDataSql(), srow.getItemMap()));
                    boolean matchData = false;
                    for (DataColumnReportEx columnReportEx : columnEx.getCalColumns())
                    {
                        matchData = matchData || columnReportEx.isMatchData();
                        if (matchData)
                        {
                            break;
                        }
                    }
                    columnEx.setMatchData(matchData);
                }
            }
        }
    }

    /**
     * 填充有Y维度的数据
     * @param tempRow   结构DataTable中的行
     * @param dataRow   数据行
     * @param cacheMap  缓存数据Map
     * @param zbCacheMap  占比缓存map
     */
    private void fillHasyRow(DataRowReportEx tempRow, DataRow dataRow, Map<String, Object> cacheMap, Map<DataColumn, Set<DataRow>> zbCacheMap)
    {
        DataColumnReportEx columnEx = null;
        
        // 缓存条件的结果
        Map<ConditionInfo, Boolean> conditionResultMap = new HashMap<ConditionInfo, Boolean>(); 
        // 跳跃数
        int skipCount = 0;
        // xNoRepeat的符合列数
        int xNoRepeatCount = -1;
        for (DataColumn dataColumn : structTable.getColumns())
        {
            if (xNoRepeatCount > -1)
            {
                xNoRepeatCount --;
            }
            // 复合数到0之后可以直接跳出循环
            if (xNoRepeatCount == 0)
            {
                break;
            }
            // 大于1的原因是因为判断跳跃之前已经循环了一次
            if (skipCount > 1)
            {
                skipCount --;
                continue;
            }
            if (!(dataColumn instanceof DataColumnReportEx))
            {
                continue;
            }
            columnEx = (DataColumnReportEx) dataColumn;
            // 计算列暂时过滤,其他维度名称列过滤,备注列,不是普通列或者列维度指标的过滤
            if (!ModelFieldTypeEnum.isCommonOrGroupField(columnEx.getFieldType()))
            {
                columnEx.setMatchData(true);
                continue;
            }
            // 匹配当前条件
            TwoTuple<Boolean, Integer> twoTuple = checkMatchCondition(dataRow.getItemMap(), columnEx.getConditions(), conditionResultMap);
            if (!twoTuple.getA())
            {
                skipCount = twoTuple.getB();
                continue;
            }
            // 设置匹配标记
            columnEx.setMatchData(true);
            String countKey = columnEx.getColumnName() + FieldConsts.STR_COUNT;
            if (!cacheMap.containsKey(countKey))
            {
                cacheMap.put(countKey, 1);
            } else
            {
                cacheMap.put(countKey, CoreUtil.parseInt(cacheMap.get(countKey)) + 1);
            }
            // 记住该列需要的占比
            zbCacheMap.get(dataColumn).add(dataRow);
            // 处理totalType
            TotalTypeEnum.handleTotalType(tempRow, columnEx, cacheMap, dataRow);
            
            // 如果是在X方向不重复维度，则可以直接跳出循环
            if (model.isxNoRepeat() && xNoRepeatCount == -1)
            {
                xNoRepeatCount = twoTuple.getB();
            }
        }
    }
    
    /**
     * 填充没有Y维度的数据(一行记录就是一条数据)
     * @param map
     * @param rowIndex
     */
    private DataRow fillNoyRow(Map<String, Object> map, int rowIndex)
    {
        
        DataRowReportEx newRow = new DataRowReportEx();
        newRow.setRowNum(rowIndex);
        newRow.initItemMap(table);
        newRow.setMatchData(true);
        newRow.setDisplay(true);
        DataColumnReportEx columnEx = null;
        
        // 缓存条件的结果
        Map<ConditionInfo, Boolean> conditionResultMap = new HashMap<ConditionInfo, Boolean>();
        // 跳跃数
        int skipCount = 0;
        // xNoRepeat的符合列数
        int xNoRepeatCount = -1;
        for (DataColumn dataColumn : structTable.getColumns())
        {
            if (xNoRepeatCount > -1)
            {
                xNoRepeatCount --;
            }
            // 复合数到0之后可以直接跳出循环
            if (xNoRepeatCount == 0)
            {
                break;
            }
            // 大于1的原因是因为判断跳跃之前已经循环了一次
            if (skipCount > 1)
            {
                skipCount --;
                continue;
            }
            if (!(dataColumn instanceof DataColumnReportEx))
            {
                continue;
            }
            columnEx = (DataColumnReportEx) dataColumn;
            // 计算列暂时过滤
            if (CoreUtil.isEmpty(columnEx.getFieldType()) || ModelFieldTypeEnum.计算列.toString().equals(columnEx.getFieldType()))
            {
                // 匹配上了的列
                columnEx.setMatchData(true);
                continue;
            }
            // 匹配当前条件
            TwoTuple<Boolean, Integer> twoTuple = checkMatchCondition(map, columnEx.getConditions(), conditionResultMap);
            if (!twoTuple.getA())
            {
                skipCount = twoTuple.getB();
                continue;
            }
            // 匹配上了的列
            columnEx.setMatchData(true);
            // 赋值
            if (columnEx.isExpType())
            {
                newRow.setValue(columnEx, CoreUtil.calculateExByAviWithAbs(columnEx.getFieldDataSql(), map));
            } else
            {
                newRow.setValue(columnEx, map.get(columnEx.getFieldDataSql()));
            }
            // 如果是X方向不重复维度，则直接跳出
            if (model.isxNoRepeat() && xNoRepeatCount == -1)
            {
                xNoRepeatCount = twoTuple.getB();
            }
        }
        // 处理计算列
        for (DataColumn dataColumn : structTable.getColumns())
        {
            if (!(dataColumn instanceof DataColumnReportEx))
            {
                continue;
            }
            columnEx = (DataColumnReportEx) dataColumn;
            if (ModelFieldTypeEnum.计算列.toString().equals(columnEx.getFieldType()))
            {
                newRow.setValue(dataColumn, CoreUtil.calculateExByAviWithAbs(columnEx.getFieldDataSql(), newRow.getItemMap()));
            }
        }
        structTable.getRows().add(newRow);
        return newRow;
    }

    /**
     * 检查targetInfo中的值是否和conditions中的条件匹配
     * @param targetInfo
     * @param conditions
     * @param conditionResultMap
     * @return
     */
    private TwoTuple<Boolean, Integer> checkMatchCondition(Map<String, Object> map, List<ConditionInfo> conditions, Map<ConditionInfo, Boolean> conditionResultMap)
    {
        if (map == null || map.isEmpty())
        {
            return new TwoTuple<Boolean, Integer>(false, 0);
        }
        // 没有条件则直接返回true
        if (conditions == null || conditions.isEmpty())
        {
            return new TwoTuple<Boolean, Integer>(true, 0);
        }
        // 细项条件
        String[] arr = null;
        Object match = null;
        Object temp = null;
        Map<String, Object> tempMap = new HashMap<String, Object>();
        for (ConditionInfo conditionInfo : conditions)
        {
            // 同一个conditionInfo只要有一个符合要求就校验通过：如电压等级=110,220,330
            // 默认是校验不通过
            boolean pass = false;
            if (conditionResultMap.containsKey(conditionInfo))
            {
                pass = conditionResultMap.get(conditionInfo);
            } else
            {
                if (StringUtil.isEmpty(conditionInfo.getKey()) || CoreUtil.isEmpty(conditionInfo.getRelation()))
                {
                    continue;
                }
                if (conditionInfo.isExpType())
                {
                    match = CoreUtil.calculateExByAviWithAbs(conditionInfo.getKey(), map);
                } else
                {
                    match = map.get(conditionInfo.getKey());
                }
                // 可能会有多个
                arr = CoreUtil.split(conditionInfo.getValue(), FieldConsts.LEVELTHREESPLIT);
                if (arr == null || arr.length == 0)
                {
                    continue;
                }
                for (String string : arr)
                {
                    if (FieldDataTypeEnum.日期.toString().equals(conditionInfo.getDataType()))
                    {
                        temp = CoreUtil.parseDate(string);
                        // 空值当做最大值
                        if (temp == null)
                        {
                            temp = CoreUtil.DATETIME_QUERY_MAX;
                        }
                        match = CoreUtil.parseDate(match);
                        if (match == null)
                        {
                            match = CoreUtil.DATETIME_QUERY_MAX;
                        }
                    } else if (FieldDataTypeEnum.数值.toString().equals(conditionInfo.getDataType()))
                    {
                        // cb 2017 09 04 兼容配成了数值但是又不是数值的问题
                        if (!CoreUtil.isNumber(string))
                        {
                            temp = string;
                            match = CoreUtil.parseStr(match);
                        } else
                        {
                            temp = CoreUtil.parseDbl(string);
                            match = CoreUtil.parseDbl(match);
                        }
                    } else 
                    {
                        temp = string;
                        match = CoreUtil.parseStr(match);
                    }
                    // 用于临时预算
                    tempMap.put("A", temp);
                    tempMap.put("B", match);
                    
                    // 同一个conditionInfo只要有一个符合要求就校验通过:如电压等级=110,220,330
                    if (CoreUtil.calExpFlag("B" + conditionInfo.getRelation() + "A", tempMap))
                    {
                        pass = true;
                        break;
                    }
                }
                conditionResultMap.put(conditionInfo, pass);
            }
            if (!pass)
            {
                return new TwoTuple<Boolean, Integer>(false, conditionInfo.getSkipCount());
            }
        }
        return new TwoTuple<Boolean, Integer>(true, conditions.get(conditions.size() - 1).getSkipCount());
    }

    @Override
    public void setStructTable(DataTable structTable)
    {
        this.structTable = structTable;
    }

    @Override
    public void setSourceTable(DataTable sourceTable)
    {
        this.table = sourceTable;
    }

    @Override
    public void setModel(DataAnalyseModel model)
    {
        this.model = model;
    }

}